package com.ruoyi.data.service.impl;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.ClassificationType;
import com.ruoyi.common.enums.DataListStatusType;
import com.ruoyi.common.enums.OrderType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.data.domain.DataList;
import com.ruoyi.data.domain.bo.DataClassificationBo;
import com.ruoyi.data.domain.bo.getData.*;
import com.ruoyi.data.domain.vo.DataClassificationVo;
import com.ruoyi.data.mapper.DataListMapper;
import com.ruoyi.data.service.IDataClassificationService;
import com.ruoyi.data.service.IDataListService;
import com.ruoyi.data.service.IGetDataService;
import com.ruoyi.system.service.ISysUserService;
import com.xxl.job.core.context.XxlJobHelper;
import lombok.RequiredArgsConstructor;
import org.redisson.api.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 获取数据操作
 *
 * @author ruoyi
 * @date 2023-10-26
 */
@RequiredArgsConstructor
@Service
public class GetDataServiceImpl implements IGetDataService {

    private final ISysUserService userService;
    private final IDataClassificationService iDataClassificationService;
    private final IDataListService iDataListService;
    private final DataListMapper dataListMapper;


    @Override
    public Boolean addDataClassification(GetDataClassificationBo bo) {

        LoginUser sysUser =  userService.getUserByToken(bo.getToken());
        DataClassificationBo add = BeanUtil.toBean(bo, DataClassificationBo.class);

        add.setClassificationType(ClassificationType.NOT_SHARE.getClassificationType());
        //二十个空字段
        add.setTableHead("[{\"dataName\":\"data1\",\"dataType\":1,\"dataValue\":\"字段1\"},{\"dataName\":\"data2\",\"dataType\":1,\"dataValue\":\"字段2\"},{\"dataName\":\"data3\",\"dataType\":1,\"dataValue\":\"字段3\"},{\"dataName\":\"data4\",\"dataType\":1,\"dataValue\":\"字段4\"},{\"dataName\":\"data5\",\"dataType\":1,\"dataValue\":\"字段5\"},{\"dataName\":\"data6\",\"dataType\":1,\"dataValue\":\"字段6\"},{\"dataName\":\"data7\",\"dataType\":1,\"dataValue\":\"字段7\"},{\"dataName\":\"data8\",\"dataType\":1,\"dataValue\":\"字段8\"},{\"dataName\":\"data9\",\"dataType\":1,\"dataValue\":\"字段9\"},{\"dataName\":\"data10\",\"dataType\":1,\"dataValue\":\"字段10\"},{\"dataName\":\"data11\",\"dataType\":1,\"dataValue\":\"字段11\"},{\"dataName\":\"data\",\"dataType\":1,\"dataValue\":\"字段12\"},{\"dataName\":\"data13\",\"dataType\":1,\"dataValue\":\"字段13\"},{\"dataName\":\"data14\",\"dataType\":1,\"dataValue\":\"字段14\"},{\"dataName\":\"data15\",\"dataType\":1,\"dataValue\":\"字段15\"},{\"dataName\":\"data16\",\"dataType\":1,\"dataValue\":\"字段16\"},{\"dataName\":\"data17\",\"dataType\":1,\"dataValue\":\"字段17\"},{\"dataName\":\"data18\",\"dataType\":1,\"dataValue\":\"字段18\"},{\"dataName\":\"data19\",\"dataType\":1,\"dataValue\":\"字段19\"},{\"dataName\":\"data20\",\"dataType\":1,\"dataValue\":\"字段20\"}]");
        return iDataClassificationService.insertByBo(add, sysUser.getUserId());

    }
    @Override
    public DataList getOneDataList(GetDataDataListBo bo) throws Exception {
        LoginUser sysUser =  userService.getUserByToken(bo.getToken());

        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), bo);

        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }

        bo.setClassificationId(dList.get(0).getClassificationId());
        DataList oneDataList = iDataListService.getOneDataList(bo, sysUser.getUserId());

        //取出部分字段
        if (ObjectUtil.isNotNull(oneDataList) && StringUtils.isNotEmpty(bo.getFieldName())){
            oneDataList = getObjByFieldName(oneDataList, bo.getFieldName());
        }

        return oneDataList;
    }

    /**
     * 根据字段名获取需要的字段
     * @return
     */
    private DataList getObjByFieldName(DataList oneDataList, String fieldName){

        DataList fieldData = new DataList();
        fieldData.setDataId(oneDataList.getDataId());
        fieldData.setClassificationId(oneDataList.getClassificationId());
        fieldData.setUserId(oneDataList.getUserId());
        fieldData.setStatus(oneDataList.getStatus());
        fieldData.setPid(oneDataList.getPid());

        fieldData.setCreateBy(oneDataList.getCreateBy());
        fieldData.setCreateTime(oneDataList.getCreateTime());
        fieldData.setUpdateBy(oneDataList.getUpdateBy());
        fieldData.setUpdateTime(oneDataList.getUpdateTime());


        //反射获取指定字段
        String[] split = fieldName.split(",");
        for (String s : split){
            try {
                s = capitalizeFirstLetter(s);
                String data =  ReflectUtil.invoke(oneDataList, "get" + s);
                ReflectUtil.invoke(fieldData, "set" + s, data);
            } catch (Exception e){
                throw new ServiceException("传入的fieldName 格式不正确! fieldName:" + Arrays.toString(split));
            }
        }

        return fieldData;
    }

    /**
     * 首字母转大写
     * @param str
     * @return
     */
    private String capitalizeFirstLetter(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }

        char firstChar = str.charAt(0);
        if (Character.isUpperCase(firstChar)) {
            return str;
        }

        return Character.toUpperCase(firstChar) + str.substring(1);
    }

    @Override
    public Long getOneDataListCount(GetDataDataListBo bo) {

        LoginUser sysUser =  userService.getUserByToken(bo.getToken());
        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), bo);
        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }
        Long classificationId = dList.get(0).getClassificationId();
        LambdaQueryWrapper<DataList> dataListLqw = Wrappers.lambdaQuery();

        dataListLqw.eq(DataList::getClassificationId, classificationId);
        dataListLqw.eq(StringUtils.isNotBlank(bo.getStatus()), DataList::getStatus, bo.getStatus());
        dataListLqw.eq(!LoginHelper.isAdmin(), DataList::getUserId, sysUser.getUserId());

        return dataListMapper.selectCount(dataListLqw);
    }

    @Override
    public TableDataInfo<DataList> queryPageList(GetDataDataListBo bo, PageQuery pageQuery) {

        LoginUser sysUser =  userService.getUserByToken(bo.getToken());
        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), bo);
        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }
        Long classificationId = dList.get(0).getClassificationId();


        LambdaQueryWrapper<DataList> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DataList::getUserId, sysUser.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), DataList::getStatus, bo.getStatus());
        lqw.eq(bo.getClassificationId() != null, DataList::getClassificationId, classificationId);

        //排序类型
        if (StringUtils.equals(OrderType.DESC.getOrderType(), bo.getOrderType())) {
            lqw.orderByDesc(BaseEntity::getCreateTime);
        } else if (StringUtils.equals(OrderType.ASC.getOrderType(), bo.getOrderType())){
            lqw.orderByAsc(BaseEntity::getCreateTime);
        } else {
            //s.order("create_time");
        }

        Page<DataList> result = dataListMapper.selectPage(pageQuery.build(), lqw);

        List<DataList> records = result.getRecords();
        List<DataList> _records = new ArrayList<>();

        if (ObjectUtil.isNotNull(records) && StringUtils.isNotEmpty(bo.getFieldName())){
            records.forEach( r -> {
                _records.add(getObjByFieldName(r, bo.getFieldName()));
            });
            records = _records;
        }

        return TableDataInfo.build(records);
    }

    @Override
    @Transactional
    public Boolean delDataClassification(GetDataClassificationBo bo) {
        LoginUser sysUser =  userService.getUserByToken(bo.getToken());

        GetDataDataListBo cbo = BeanUtil.toBean(bo, GetDataDataListBo.class);
        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), cbo);

        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }
        Long classificationId = dList.get(0).getClassificationId();

        //删除分类
        iDataClassificationService.deleteWithValidByIds(sysUser.getUserId(), Arrays.asList(classificationId), false);

        LambdaQueryWrapper<DataList> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DataList::getUserId, sysUser.getUserId());
        lqw.eq(DataList::getClassificationId, classificationId);
        int delete = dataListMapper.delete(lqw);
        RedissonClient client = RedisUtils.getClient();

        String KEY = CacheConstants.ONE_DATA_KEY + sysUser.getUserId() + ":" + classificationId;
        RScoredSortedSet<String> sortedSet = client.getScoredSortedSet(KEY);
        sortedSet.delete();

        return delete > 0;
    }

    @Override
    public DataList addAndUpdateDataList(AddDataListBo bo) {
        LoginUser sysUser =  userService.getUserByToken(bo.getToken());

        GetDataDataListBo cbo = BeanUtil.toBean(bo, GetDataDataListBo.class);
        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), cbo);
        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }
        Long classificationId = dList.get(0).getClassificationId();
        DataList inBo = BeanUtil.toBean(bo, DataList.class);


        //update
        if (StringUtils.isNotEmpty(bo.getDataId())){
            DataList dataList = dataListMapper.selectById(bo.getDataId());
            //如果修改的不是自己的数据
            if (dataList == null || !Objects.equals(dataList.getUserId(), sysUser.getUserId())){
                throw new ServiceException("不是你的数据不能修改 id：" + bo.getDataId());
            }
        } else {
            inBo.setUserId(sysUser.getUserId());
            inBo.setClassificationId(classificationId);
            inBo.setStatus("0");
            inBo.setPid(IdUtil.fastSimpleUUID());
        }

        return dataListMapper.insertOrUpdate(inBo) ? inBo : null;
    }

    @Override
    @Transactional
    public int delDataList(DelDataListBo bo) {
        LoginUser sysUser =  userService.getUserByToken(bo.getToken());

        GetDataDataListBo cbo = BeanUtil.toBean(bo, GetDataDataListBo.class);
        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), cbo);
        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }

        LambdaQueryWrapper<DataList> lqw = new LambdaQueryWrapper<>();
        lqw.select(DataList::getDataId,DataList::getUserId,DataList::getClassificationId);
        lqw.eq(DataList::getUserId, sysUser.getUserId());
        lqw.in(DataList::getDataId, Arrays.asList(bo.getDataIds()));
        List<DataList> dataLists = dataListMapper.selectList(lqw);

        if (CollUtil.isEmpty(dataLists)){
            throw new ServiceException("可删除数据为空！");
        }

        int res = dataListMapper.deleteBatchIds(dataLists.stream().map(DataList::getDataId).collect(Collectors.toList()));
        //清空缓存
        iDataListService.cleatRedis(dataLists);

        return res;
    }

    @Override
    public int delDataListByClassification(DelDataListBo bo) {
        LoginUser sysUser =  userService.getUserByToken(bo.getToken());

        GetDataDataListBo cbo = BeanUtil.toBean(bo, GetDataDataListBo.class);
        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), cbo);
        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }
        Long classificationId = dList.get(0).getClassificationId();

        LambdaQueryWrapper<DataList> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DataList::getUserId, sysUser.getUserId());
        lqw.eq(DataList::getClassificationId, classificationId);
        int delete = dataListMapper.delete(lqw);
        RedissonClient client = RedisUtils.getClient();

        String KEY = CacheConstants.ONE_DATA_KEY + sysUser.getUserId() + ":" + classificationId;
        RScoredSortedSet<String> sortedSet = client.getScoredSortedSet(KEY);
        sortedSet.delete();

        return delete;
    }


    @Override
    @Transactional
    public boolean addDataListToRedis() {
        RedissonClient client = RedisUtils.getClient();
        LambdaQueryWrapper<DataList> lqw = new LambdaQueryWrapper<>();
        //测试
        //lqw.in(DataList::getUserId, "1724712904718061569","1729779501675499522");


        lqw.select(DataList::getUserId,DataList::getClassificationId);
        lqw.eq(DataList::getCacheType, "0");
        lqw.eq(DataList::getStatus, "0");
        lqw.groupBy(DataList::getUserId)
            .groupBy(DataList::getClassificationId);



        List<DataList> dataLists = dataListMapper.selectList(lqw);

        XxlJobHelper.log("检索到用户数据：:" + dataLists.size());

        dataLists.forEach( d -> {

            Long userId =  d.getUserId();
            Long classificationId = d.getClassificationId();

            String KEY = CacheConstants.ONE_DATA_KEY + userId + ":" + classificationId;

            RScoredSortedSet<Object> list = client.getScoredSortedSet(KEY);

            List<DataList> dataListsUser = dataListMapper.selectList(new LambdaQueryWrapper<DataList>()
                .select(DataList::getDataId,BaseEntity::getCreateTime)
                .eq(DataList::getUserId, userId)
                .eq(DataList::getClassificationId, classificationId)
                    .eq(DataList::getCacheType, 0)
                    .eq(DataList::getStatus, DataListStatusType.NOT_READ.getClassificationType())
                .last("limit 100000")
            );

            if (CollUtil.isEmpty(dataListsUser))
            {
                XxlJobHelper.log("用户数据为空  用户：{}", KEY);
                return;
            }

            for (DataList l : dataListsUser){
                l.setCacheType(1);
            }
            dataListMapper.updateBatchById(dataListsUser);

            dataListsUser.parallelStream().forEach(set -> {
                list.add(set.getCreateTime().getTime(), set.getDataId());
            });
            XxlJobHelper.log("已添加缓存成功  用户：{}，数量：{}", KEY , dataListsUser.size());

        });

        return false;
    }

    @Override
    public List<DataList> getOneDataListPageByFieldData(GetDataDataListBo bo, PageQuery pageQuery) {
        LoginUser sysUser =  userService.getUserByToken(bo.getToken());

        GetDataDataListBo cbo = BeanUtil.toBean(bo, GetDataDataListBo.class);
        List<DataClassificationVo> dList = this.selectClassificationByIdUserId(sysUser.getUserId(), cbo);

        if (CollUtil.isEmpty(dList)){
            throw new ServiceException("分类名称不存在！");
        }

        Long classificationId = dList.get(0).getClassificationId();

        QueryWrapper<DataList> lqw = new QueryWrapper<>();
        lqw.eq("classification_id",classificationId);
        lqw.eq("user_id",sysUser.getUserId());
        lqw.eq("status", bo.getStatus());

        if (StringUtils.equals("eq", bo.getLikeType())){
            lqw.eq(bo.getFieldName(), bo.getValue());
        } else {
            lqw.likeRight(bo.getFieldName(), bo.getValue());

        }


        if (StringUtils.equals(OrderType.DESC.getOrderType(), bo.getOrderType())) {
            lqw.orderByDesc("create_time");
            lqw.last("limit 1");
        } else if (StringUtils.equals(OrderType.ASC.getOrderType(), bo.getOrderType())){
            lqw.orderByAsc("create_time");
            lqw.last("limit 1");
        } else {
            //s.order("create_time");
        }

        return dataListMapper.selectList(lqw);
    }

    @Override
    public boolean batchPaste(BatchPasteBo bo) {
        LoginUser sysUser =  userService.getUserByToken(bo.getToken());

        GetDataDataListBo cbo = new GetDataDataListBo();
        cbo.setClassificationName(bo.getBeforeClassificationName());
        List<DataClassificationVo> beforeCList = this.selectClassificationByIdUserId(sysUser.getUserId(), cbo);
        cbo.setClassificationName(bo.getClassificationName());
        List<DataClassificationVo> cList = this.selectClassificationByIdUserId(sysUser.getUserId(), cbo);



        if (CollUtil.isEmpty(beforeCList) && CollUtil.isEmpty(cList)){
            throw new ServiceException("分类不存在  请检查");
        }

        Long beforeClassificationId = beforeCList.get(0).getClassificationId();
        Long classificationId = cList.get(0).getClassificationId();

        return iDataListService.batchPaste(sysUser.getUserId(), Arrays.asList(bo.getDataIds()),
            beforeClassificationId,classificationId,
            bo.getStatus(),
            bo.getSourceStatus());
    }

    TimedCache<String, List<DataClassificationVo>> timedCache = CacheUtil.newTimedCache(1000 * 5);

    /**
     * 缓存 分类
     * @param userId
     * @return
     */
    private List<DataClassificationVo> selectClassificationByIdUserId(Long userId, GetDataDataListBo bo){

        String KEY = userId + "-" + bo.getClassificationName();

        if (timedCache.get(KEY) != null){
            return timedCache.get(KEY);
        }

        DataClassificationBo d = new DataClassificationBo();
        d.setClassificationName(bo.getClassificationName());

        List<DataClassificationVo> dataClassificationVos = iDataClassificationService.selectClassificationByIdUserId(userId, d);
        timedCache.put(KEY, dataClassificationVos);

        return timedCache.get(KEY);

    }

}
